package com.poqop.document;

import android.content.Context;
import android.graphics.*;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.widget.Scroller;

import com.poqop.document.events.ZoomListener;
import com.poqop.document.models.CurrentPageModel;
import com.poqop.document.models.DecodingProgressModel;
import com.poqop.document.models.ZoomModel;
import com.poqop.document.multitouch.MultiTouchZoom;
import com.poqop.document.multitouch.MultiTouchZoomImpl;

import java.util.HashMap;
import java.util.Map;

public class DocumentView extends View implements ZoomListener {
	final ZoomModel zoomModel;
	private final CurrentPageModel currentPageModel;
	DecodeService decodeService;
	private final HashMap<Integer, Page> pages = new HashMap<Integer, Page>();
	private boolean isInitialized = false;
	private int pageToGoTo;
	private float lastX;
	private float lastY;
	private VelocityTracker velocityTracker;
	private final Scroller scroller;
	DecodingProgressModel progressModel;
	private RectF viewRect;
	private boolean inZoom;
	private long lastDownEventTime;
	private static final int DOUBLE_TAP_TIME = 500;
	private MultiTouchZoom multiTouchZoom;

	public DocumentView(Context context, final ZoomModel zoomModel, DecodingProgressModel progressModel, CurrentPageModel currentPageModel) {
		super(context);
		this.zoomModel = zoomModel;
		this.progressModel = progressModel;
		this.currentPageModel = currentPageModel;
		// 屏幕保持高亮
		setKeepScreenOn(true);
		scroller = new Scroller(getContext());
		setFocusable(true);
		setFocusableInTouchMode(true);
		initMultiTouchZoomIfAvailable(zoomModel);
	}

	private void initMultiTouchZoomIfAvailable(ZoomModel zoomModel) {
		try {
			multiTouchZoom = (MultiTouchZoom) Class.forName("com.poqop.document.multitouch.MultiTouchZoomImpl").getConstructor(ZoomModel.class).newInstance(zoomModel);
		} catch (Exception e) {
			System.out.println("Multi touch zoom is not available: " + e);
		}
	}

	public void setDecodeService(DecodeService decodeService) {
		this.decodeService = decodeService;
	}

	private void init() {
		if (isInitialized) {
			return;
		}
		final int width = decodeService.getEffectivePagesWidth();
		final int height = decodeService.getEffectivePagesHeight();
		// 生成页数
		for (int i = 0; i < decodeService.getPageCount(); i++) {
			pages.put(i, new Page(this, i));
			//取得纵横比
			pages.get(i).setAspectRatio(width, height);
		}
		// 标识已经初始化
		isInitialized = true;
		invalidatePageSizes();
		goToPageImpl(pageToGoTo);
	}

	/**
	 * 去往某个页面
	 * 
	 * @param toPage
	 */
	private void goToPageImpl(final int toPage) {
		scrollTo(0, pages.get(toPage).getTop());
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		// bounds could be not updated
		post(new Runnable() {
			public void run() {
				// 设置当前页
				currentPageModel.setCurrentPageIndex(getCurrentPage());
			}
		});
		// 正在缩放则返回
		if (inZoom) {
			return;
		}
		// on scrollChanged can be called from scrollTo just after new layout
		// applied so we should wait for relayout
		post(new Runnable() {
			public void run() {
				// 刷新页面
				updatePageVisibility();
			}
		});
	}

	private void updatePageVisibility() {
		for (Page page : pages.values()) {
			// 刷新成为可见页
			page.updateVisibility();
		}
	}

	public void commitZoom() {
		// 缩放
		for (Page page : pages.values()) {
			// 缩放每一页
			page.invalidate();
		}
		inZoom = false;
	}

	public void showDocument() {
		// use post to ensure that document view has width and height before
		// decoding begin
		post(new Runnable() {
			public void run() {
				// 初始化文档
				init();
				// 刷新页，是否可见
				updatePageVisibility();
			}
		});
	}

	public void goToPage(int toPage) {
		if (isInitialized) {
			goToPageImpl(toPage);
		} else {
			pageToGoTo = toPage;
		}
	}

	/**
	 * 取得当前页
	 * 
	 * @return
	 */
	public int getCurrentPage() {
		for (Map.Entry<Integer, Page> entry : pages.entrySet()) {
			if (entry.getValue().isVisible()) {// 第一个可见就是当前页
				return entry.getKey();
			}
		}
		return 0;
	}

	public void zoomChanged(float newZoom, float oldZoom) {
		inZoom = true;
		// 停止滚动
		stopScroller();
		// 新老缩放比
		final float ratio = newZoom / oldZoom;
		// 缩放每一个的比例
		invalidatePageSizes();
		// 缩放完后，滚动页面
		scrollTo((int) ((getScrollX() + getWidth() / 2) * ratio - getWidth() / 2), (int) ((getScrollY() + getHeight() / 2) * ratio - getHeight() / 2));
		postInvalidate();
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		super.onTouchEvent(ev);

		if (multiTouchZoom != null) {
			// 缩放控制
			if (multiTouchZoom.onTouchEvent(ev)) {
				return true;
			}
			//
			if (multiTouchZoom.isResetLastPointAfterZoom()) {
				// 设置最后一个event
				setLastPosition(ev);
				multiTouchZoom.setResetLastPointAfterZoom(false);
			}
		}

		if (velocityTracker == null) {
			velocityTracker = VelocityTracker.obtain();
		}
		velocityTracker.addMovement(ev);

		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			stopScroller();
			// 设置最后一个event
			setLastPosition(ev);
			if (ev.getEventTime() - lastDownEventTime < DOUBLE_TAP_TIME) {
				zoomModel.toggleZoomControls();
			} else {
				lastDownEventTime = ev.getEventTime();
			}
			break;
		case MotionEvent.ACTION_MOVE:
			scrollBy((int) (lastX - ev.getX()), (int) (lastY - ev.getY()));
			setLastPosition(ev);
			break;
		case MotionEvent.ACTION_UP:
			velocityTracker.computeCurrentVelocity(1000);
			scroller.fling(getScrollX(), getScrollY(), (int) -velocityTracker.getXVelocity(), (int) -velocityTracker.getYVelocity(), getLeftLimit(), getRightLimit(), getTopLimit(), getBottomLimit());
			velocityTracker.recycle();
			velocityTracker = null;

			break;
		}
		return true;
	}

	private void setLastPosition(MotionEvent ev) {
		lastX = ev.getX();
		lastY = ev.getY();
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			switch (event.getKeyCode()) {
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				lineByLineMoveTo(1);
				return true;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				lineByLineMoveTo(-1);
				return true;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				verticalDpadScroll(1);
				return true;
			case KeyEvent.KEYCODE_DPAD_UP:
				verticalDpadScroll(-1);
				return true;
			}
		}
		return super.dispatchKeyEvent(event);
	}

	// 上下左右键的前进
	private void verticalDpadScroll(int direction) {
		scroller.startScroll(getScrollX(), getScrollY(), 0, direction * getHeight() / 2);
		invalidate();
	}

	private void lineByLineMoveTo(int direction) {
		if (direction == 1 ? getScrollX() == getRightLimit() : getScrollX() == getLeftLimit()) {
			scroller.startScroll(getScrollX(), getScrollY(), direction * (getLeftLimit() - getRightLimit()), (int) (direction * pages.get(getCurrentPage()).bounds.height() / 50));
		} else {
			scroller.startScroll(getScrollX(), getScrollY(), direction * getWidth() / 2, 0);
		}
		invalidate();
	}

	private int getTopLimit() {
		return 0;
	}

	private int getLeftLimit() {
		return 0;
	}

	private int getBottomLimit() {
		// 最后一页
		return (int) pages.get(pages.size() - 1).bounds.bottom - getHeight();
	}

	private int getRightLimit() {
		return (int) (getWidth() * zoomModel.getZoom()) - getWidth();
	}

	@Override
	public void scrollTo(int x, int y) {
		super.scrollTo(Math.min(Math.max(x, getLeftLimit()), getRightLimit()), Math.min(Math.max(y, getTopLimit()), getBottomLimit()));
		viewRect = null;
	}

	/**
	 * 视图可见区域
	 * 
	 * @return
	 */
	RectF getViewRect() {
		if (viewRect == null) {
			viewRect = new RectF(getScrollX(), getScrollY(), getScrollX() + getWidth(), getScrollY() + getHeight());
		}
		return viewRect;
	}

	@Override
	public void computeScroll() {
		if (scroller.computeScrollOffset()) {
			// 滚动到目的地
			scrollTo(scroller.getCurrX(), scroller.getCurrY());
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 画page
		for (Page page : pages.values()) {
			page.draw(canvas);
		}
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		// 滚动和缩放比
		float scrollScaleRatio = getScrollScaleRatio();
		invalidatePageSizes();
		invalidateScroll(scrollScaleRatio);
		commitZoom();
	}

	void invalidatePageSizes() {
		if (!isInitialized) {
			return;
		}
		float heightAccum = 0;
		int width = getWidth();
		// 缩放大小
		float zoom = zoomModel.getZoom();
		for (int i = 0; i < pages.size(); i++) {
			Page page = pages.get(i);
			// 缩放高度比例，相对于宽
			float pageHeight = page.getPageHeight(width, zoom);
			// 设置这一页的矩形（即页的大小）
			page.setBounds(new RectF(0, heightAccum, width * zoom, heightAccum + pageHeight));
			heightAccum += pageHeight;
		}
	}

	private void invalidateScroll(float ratio) {
		if (!isInitialized) {
			return;
		}
		stopScroller();
		final Page page = pages.get(0);
		if (page == null || page.bounds == null) {
			return;
		}
		// 缩放后的滚动
		scrollTo((int) (getScrollX() * ratio), (int) (getScrollY() * ratio));
	}

	private float getScrollScaleRatio() {
		final Page page = pages.get(0);
		if (page == null || page.bounds == null) {
			return 0;
		}
		final float z = zoomModel.getZoom();
		return getWidth() * z / page.bounds.width();
	}

	private void stopScroller() {
		if (!scroller.isFinished()) {
			scroller.abortAnimation();
		}
	}

}
